using System;
using System.Collections.Generic;
using Common;
using Godot;
namespace DRE.Character {
	public class Manager : IRegisterManager<Character> {
		readonly Dictionary<string, Character> characters = new Dictionary<string, Character>();
		public bool IsExists(string id) => characters.ContainsKey(id);
		public void Register(Character character) {
			if (IsExists(character.Id)) {
				throw new ArgumentException("A character with the same ID: " + character.Id + " is already registered!");
			}
			characters.Add(character.Id, character);
		}
		public void Clear() => characters.Clear();
		public Character Get(string id) => IsExists(id) ? characters[id] : null;
		public void Remove(string id) => characters.Remove(id);
		public Character this[string id] => Get(id);
	}

	//Characters are readonly objects.
	//DO NOT leave any codes that stores data here. All data should be stored in the storage.
	public abstract class Character : IRegisterable {
		public static ComplexValue.Dictionary GetStorage(string id) => DRE.Manager.Storages["Static"].Root.GetOrCreateDictionary("Characters").GetOrCreateDictionary(id);

		public Character(bool initializeStorage = true) {
			InitializeNonStorage();
			if (initializeStorage) {
				InitializeStorage();
			}
		}

		/// <summary>
		/// Get the storage dictionary of this <see cref="Character"/>.
		/// </summary>
		/// <returns></returns>
		public ComplexValue.Dictionary Storage => Character.GetStorage(Id);

		//Stoarge-unrelated properties
		//These are plain values.
		//Initialize those properties in InitializeCustomNonStorage().
		public Tag Tags { get; } = new Tag();
		public abstract string Id { get; }
		public virtual string Name { get; set; }
		public virtual float MaxHp { get; set; }
		public virtual float Attack { get; set; }
		public virtual float Defense { get; set; }
		public virtual float Magic { get; set; }
		public virtual string Description { get; set; }

		//Storage-related properties
		//These will be written to/read from the storage.
		//Initialize those properties in InitializeCustomStorage().
		public float Hp {
			get => Storage["Hp"].AsFloat(1);
			set => Storage["Hp"] = value;
		}
		public ComplexValue.Dictionary Equipments => Storage.GetOrCreateDictionary("Equipments");
		public ComplexValue.List Spells => Storage.GetOrCreateList("Spells");
		public virtual ComplexValue.List Status => Storage.GetOrCreateList("Status");


		/// <summary>
		/// Initialize storage-related properties.
		/// This will overwrite the data in the storage.
		/// </summary>
		public virtual void InitializeStorage() {
			Hp = MaxHp;
			Status[0] = "Unknown";
			Status[1] = "Unknown";
			Status[2] = "Unknown";
			Equipments["Weapon"] = "";
			Equipments["Armor0"] = "";
			Equipments["Armor1"] = "";
		}

		/// <summary>
		/// Initialize storage-unrelated properties.
		/// </summary>
		protected virtual void InitializeNonStorage() {

		}
	}

	public struct EquipmentPosition {
		public EquipmentPosition(string character, string equipment) {
			Character = character;
			Equipment = equipment;
		}
		public string Character { get; set; }
		public string Equipment { get; set; }
	}

	public class SpellManager : IRegisterManager<Spell> {
		public SpellManager() {
			Register(new Spell("Null") {
				Name = "{Color Red}Null",
				Description = "Somewhere in your code used\na non-existing spell ID.",
				Tp = 99
			});
		}

		readonly Dictionary<string, Spell> spellItems = new Dictionary<string, Spell>();
		public bool IsExists(string id) => spellItems.ContainsKey(id);
		public void Register(Spell ability) {
			if (IsExists(ability.Id)) {
				throw new ArgumentException("An ability with the same ID: " + ability.Id + " is already registered!");
			}
			spellItems.Add(ability.Id, ability);
		}
		public void Clear() => spellItems.Clear();
		public Spell Get(string id) => IsExists(id) ? spellItems[id] : spellItems["Null"];
		public void Remove(string id) => spellItems.Remove(id);
		public Spell this[string id] => Get(id);
	}
	public struct Spell : IRegisterable {
		public Spell(string id) {
			Id = id;
			Tp = 0;
			Name = "";
			Description = "";
		}
		public string Id { get; }
		public float Tp { get; set; }
		public string Name { get; set; }
		public string Description { get; set; }
	}

	public class StatsItemManager : IRegisterManager<StatsItem> {
		public StatsItemManager() {
			Register(new StatsItem("Null") {
				Name = "{Color Red}Non-existing ID."
			});
			Register(new StatsItem("Unknown") {
				Name = "{Color Gray}???"
			});
			Register(new StatsItem("NoAbility") {
				Name = "{Color Gray}(No Ability.)"
			});
		}

		readonly Dictionary<string, StatsItem> statsItems = new Dictionary<string, StatsItem>();
		public bool IsExists(string id) => statsItems.ContainsKey(id);
		public void Register(StatsItem statsItem) {
			if (IsExists(statsItem.Id)) {
				throw new ArgumentException("An stats item with the same ID: " + statsItem.Id + " is already registered!");
			}
			statsItems.Add(statsItem.Id, statsItem);
		}
		public void Clear() => statsItems.Clear();
		public StatsItem Get(string id) => IsExists(id) ? statsItems[id] : statsItems["Unknown"];
		public void Remove(string id) => statsItems.Remove(id);
		public StatsItem this[string id] => Get(id);
	}
	public struct StatsItem : IRegisterable {
		public StatsItem(string id) {
			Id = id;
			Icon = "";
			IconColor = Colors.White;
			Name = "";
			Value = "";
			ValueOffset = 0;
		}

		public string Id { get; }
		public string Icon { get; set; }
		public Color IconColor { get; set; }
		public string Name { get; set; }
		public string Value { get; set; }
		public float ValueOffset { get; set; }
	}
}